# -*- coding: utf-8 -*-
"""
参数验证工具模块
@api-version: 1.0.0
// REF: API-VALIDATOR-001
"""

import re
import os
from typing import Dict, Any, List, Optional, Union
from werkzeug.datastructures import FileStorage
from flask import request

from backend.utils.api_response import ApiResponse

class ValidationError(Exception):
    """验证错误异常"""
    
    def __init__(self, message: str, field: str = None, errors: Dict = None):
        self.message = message
        self.field = field
        self.errors = errors or {}
        super().__init__(message)

class Validator:
    """参数验证器基类"""
    
    def __init__(self, data: Dict[str, Any]):
        self.data = data
        self.errors = {}
    
    def validate(self) -> bool:
        """执行验证
        
        Returns:
            bool: 验证是否通过
        """
        return len(self.errors) == 0
    
    def get_errors(self) -> Dict[str, List[str]]:
        """获取验证错误
        
        Returns:
            Dict: 错误信息字典
        """
        return self.errors
    
    def add_error(self, field: str, message: str):
        """添加验证错误
        
        Args:
            field: 字段名
            message: 错误消息
        """
        if field not in self.errors:
            self.errors[field] = []
        self.errors[field].append(message)
    
    def required(self, field: str, message: str = None) -> 'Validator':
        """验证必填字段
        
        Args:
            field: 字段名
            message: 自定义错误消息
        
        Returns:
            Validator: 验证器实例
        """
        if field not in self.data or self.data[field] is None or str(self.data[field]).strip() == '':
            self.add_error(field, message or f'{field}不能为空')
        return self
    
    def string(self, field: str, min_length: int = None, max_length: int = None, 
               message: str = None) -> 'Validator':
        """验证字符串字段
        
        Args:
            field: 字段名
            min_length: 最小长度
            max_length: 最大长度
            message: 自定义错误消息
        
        Returns:
            Validator: 验证器实例
        """
        if field in self.data and self.data[field] is not None:
            value = str(self.data[field])
            
            if min_length is not None and len(value) < min_length:
                self.add_error(field, message or f'{field}长度不能少于{min_length}个字符')
            
            if max_length is not None and len(value) > max_length:
                self.add_error(field, message or f'{field}长度不能超过{max_length}个字符')
        
        return self
    
    def integer(self, field: str, min_value: int = None, max_value: int = None,
                message: str = None) -> 'Validator':
        """验证整数字段
        
        Args:
            field: 字段名
            min_value: 最小值
            max_value: 最大值
            message: 自定义错误消息
        
        Returns:
            Validator: 验证器实例
        """
        if field in self.data and self.data[field] is not None:
            try:
                value = int(self.data[field])
                
                if min_value is not None and value < min_value:
                    self.add_error(field, message or f'{field}不能小于{min_value}')
                
                if max_value is not None and value > max_value:
                    self.add_error(field, message or f'{field}不能大于{max_value}')
                    
            except (ValueError, TypeError):
                self.add_error(field, message or f'{field}必须是整数')
        
        return self
    
    def email(self, field: str, message: str = None) -> 'Validator':
        """验证邮箱格式
        
        Args:
            field: 字段名
            message: 自定义错误消息
        
        Returns:
            Validator: 验证器实例
        """
        if field in self.data and self.data[field] is not None:
            email_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
            if not re.match(email_pattern, str(self.data[field])):
                self.add_error(field, message or f'{field}格式不正确')
        
        return self
    
    def in_list(self, field: str, valid_values: List[Any], 
                message: str = None) -> 'Validator':
        """验证字段值是否在指定列表中
        
        Args:
            field: 字段名
            valid_values: 有效值列表
            message: 自定义错误消息
        
        Returns:
            Validator: 验证器实例
        """
        if field in self.data and self.data[field] is not None:
            if self.data[field] not in valid_values:
                self.add_error(field, message or f'{field}必须是以下值之一: {", ".join(map(str, valid_values))}')
        
        return self
    
    def regex(self, field: str, pattern: str, message: str = None) -> 'Validator':
        """验证正则表达式
        
        Args:
            field: 字段名
            pattern: 正则表达式模式
            message: 自定义错误消息
        
        Returns:
            Validator: 验证器实例
        """
        if field in self.data and self.data[field] is not None:
            if not re.match(pattern, str(self.data[field])):
                self.add_error(field, message or f'{field}格式不正确')
        
        return self

def validate_json(required_fields=None, optional_fields=None):
    """验证JSON请求数据的装饰器
    
    Args:
        required_fields: 必需字段列表
        optional_fields: 可选字段列表
    """
    from functools import wraps
    
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            from flask import request
            
            if not request.is_json:
                return ApiResponse.error('请求必须是JSON格式', 400).to_dict()
            
            data = request.get_json()
            if data is None:
                return ApiResponse.error('无效的JSON数据', 400).to_dict()
            
            # 验证必需字段
            if required_fields:
                for field in required_fields:
                    if field not in data:
                        return ApiResponse.error(f'缺少必需字段: {field}', 400).to_dict()
            
            # 将验证后的数据传递给视图函数
            return f(data, *args, **kwargs)
        return decorated_function
    return decorator

def validate_file_upload(file_field: str = 'file', 
                        allowed_extensions: List[str] = None,
                        max_size: int = None) -> FileStorage:
    """验证文件上传
    
    Args:
        file_field: 文件字段名
        allowed_extensions: 允许的文件扩展名列表
        max_size: 最大文件大小（字节）
    
    Returns:
        FileStorage: 上传的文件对象
    
    Raises:
        ValidationError: 验证失败时抛出
    """
    if file_field not in request.files:
        raise ValidationError(f"缺少文件字段: {file_field}")
    
    file = request.files[file_field]
    
    if not file or file.filename == '':
        raise ValidationError("未选择文件")
    
    # 验证文件扩展名
    if allowed_extensions:
        file_ext = os.path.splitext(file.filename)[1].lower().lstrip('.')
        if file_ext not in [ext.lower().lstrip('.') for ext in allowed_extensions]:
            raise ValidationError(f"不支持的文件类型，允许的类型: {', '.join(allowed_extensions)}")
    
    # 验证文件大小
    if max_size:
        # 获取文件大小
        file.seek(0, os.SEEK_END)
        file_size = file.tell()
        file.seek(0)  # 重置文件指针
        
        if file_size > max_size:
            max_size_mb = max_size / (1024 * 1024)
            raise ValidationError(f"文件大小不能超过 {max_size_mb:.1f}MB")
    
    return file

def validate_pagination(default_page: int = 1, default_page_size: int = 20,
                       max_page_size: int = 100) -> Dict[str, int]:
    """验证分页参数
    
    Args:
        default_page: 默认页码
        default_page_size: 默认页大小
        max_page_size: 最大页大小
    
    Returns:
        Dict: 包含page和page_size的字典
    
    Raises:
        ValidationError: 验证失败时抛出
    """
    try:
        page = int(request.args.get('page', default_page))
        page_size = int(request.args.get('pageSize', default_page_size))
        
        if page < 1:
            raise ValidationError("页码必须大于0")
        
        if page_size < 1:
            raise ValidationError("页大小必须大于0")
        
        if page_size > max_page_size:
            raise ValidationError(f"页大小不能超过{max_page_size}")
        
        return {'page': page, 'page_size': page_size}
        
    except ValueError:
        raise ValidationError("页码和页大小必须是整数")

def validate_file_path(path: str) -> str:
    """验证文件路径安全性
    
    Args:
        path: 文件路径
    
    Returns:
        str: 安全的文件路径
    
    Raises:
        ValidationError: 路径不安全时抛出
    """
    if not path:
        raise ValidationError("路径不能为空")
    
    # 检查路径遍历攻击
    if '..' in path or path.startswith('/'):
        raise ValidationError("路径不安全")
    
    # 移除多余的斜杠
    path = re.sub(r'/+', '/', path)
    
    return path.strip('/')

def validate_md5(md5_hash: str) -> str:
    """验证MD5哈希值格式
    
    Args:
        md5_hash: MD5哈希值
    
    Returns:
        str: 验证后的MD5哈希值
    
    Raises:
        ValidationError: 格式不正确时抛出
    """
    if not md5_hash:
        raise ValidationError("MD5哈希值不能为空")
    
    if not re.match(r'^[a-fA-F0-9]{32}$', md5_hash):
        raise ValidationError("MD5哈希值格式不正确")
    
    return md5_hash.lower()